using System.Drawing;
using GraphDrawing2.Canvas;
using GraphDrawing2.V2.Concrete.Model;

namespace GraphDrawing2.V2.Graph.Model
{
    public interface IVisualElement : IVisualObject
    {
        ElementsModel Owner { get; set; }
    }

    public interface IMoveable
    {
        void Move(float dx, float dy);
    }

    public interface IInteractive
    {
        IHitTestResult HitTest(PointF point);
    }

    public interface IZOrdered
    {
        void BringToFront();
    }

    public interface IRenderable
    {
        void Render(DrawingContext dc);
    }

    public interface IInitalizable<in T>
    {
        void Initialize(T model);
    }

    public abstract class VisualElement<TModel, TView> : IVisualElement, IInteractive, IMoveable, IRenderable where TModel: class, IVisualItem where TView : IVisualElementView, IInitalizable<TModel> 
    {
        protected readonly TModel model;
        protected readonly TView view;

        public ElementsModel Owner { get; set; }

        protected VisualElement()
        {
            model = CreateModel();
            view = CreateView();
            view.Initialize(model);
        }

        protected abstract TModel CreateModel();
        protected abstract TView CreateView();
        protected abstract IHitTestResult HitTestInternal(PointF localPoint);

        public StateHints Hints
        {
            get { return model.Hints; }
            set { model.Hints = value; }
        }

        public void Move(float dx, float dy)
        {
            model.Position = model.Position + new SizeF(dx, dy);
        }

        public IHitTestResult HitTest(PointF point)
        {
            var local = point.RelativeToOrigin(model.Position);
            return HitTestInternal(local);
        }

        public void Render(DrawingContext dc)
        {
            view.Render(dc);
        }
    }

    public class VisualElementModel : IVisualItem
    {
        public PointF Position { get; set; }
        public StateHints Hints { get; set; }
    }

    public static class VisualElementControllerExt
    {
        public static void SetHint(this IVisualElement element, StateHints hints)
        {
            element.Hints |= hints;
        }

        public static void ClearHint(this IVisualElement element, StateHints hints)
        {
            element.Hints &= ~hints;
        }

        public static bool IsSelected(this IVisualElement element)
        {
            return element.Hints.HasFlag(StateHints.SELECTED);
        }

        public static bool IsHovered(this IVisualElement element)
        {
            return element.Hints.HasFlag(StateHints.HOVERED);
        }
    }
}